#include <stdio.h>
#include "esp_log.h"
#include "driver/i2c.h"
#include "./codetab.h"

static const char *TAG = "maziot: i2c-oled";


#define I2C_MASTER_SCL_IO   22
#define I2C_MASTER_SDA_IO   21
#define I2C_MASTER_NUM       0
#define I2C_MASTER_FREQ_HZ          400000
#define I2C_MASTER_TX_BUF_DISABLE   0
#define I2C_MASTER_RX_BUF_DISABLE   0
#define OLED_ADDRESS    0x3c  // I2C tools read addr
#define I2C_MASTER_TIMEOUT_MS       1000

static esp_err_t i2c_master_init(void)
{
	int i2c_master_port = I2C_MASTER_NUM;

	i2c_config_t conf = {
		.mode = I2C_MODE_MASTER,
		.sda_io_num = I2C_MASTER_SDA_IO,
		.scl_io_num = I2C_MASTER_SCL_IO,
		.sda_pullup_en = GPIO_PULLUP_ENABLE,
		.scl_pullup_en = GPIO_PULLUP_ENABLE,
		.master.clk_speed = I2C_MASTER_FREQ_HZ,
	};

	i2c_param_config(i2c_master_port, &conf);

	return i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
}

static int WriteDat(unsigned char I2C_Data)  //写数据
{
	int ret;
	uint8_t write_buf[2];
	write_buf[0] = 0x40;
	write_buf[1] = I2C_Data;
	ret = i2c_master_write_to_device(I2C_MASTER_NUM, OLED_ADDRESS, write_buf, sizeof(write_buf), I2C_MASTER_TIMEOUT_MS / portTICK_RATE_MS);

	if(ret)
		ESP_LOGI(TAG, "WriteDat error");
	
	return ret;
}

static int WriteCmd(unsigned char I2C_Command)  //写命令
{
	int ret;
	uint8_t write_buf[2];
	write_buf[0] = 0x00;
	write_buf[1] = I2C_Command;
	ret = i2c_master_write_to_device(I2C_MASTER_NUM, OLED_ADDRESS, write_buf, sizeof(write_buf), I2C_MASTER_TIMEOUT_MS / portTICK_RATE_MS);
	if(ret)
		ESP_LOGI(TAG, "WriteCmd error");
	return ret;
}


void OLED_Fill(unsigned char fill_Data)//全屏填充
{
	unsigned char m,n;
	for(m=0;m<8;m++)
	{
		WriteCmd(0xb0+m);       //page0-page1
		WriteCmd(0x00);         //low column start address
		WriteCmd(0x10);         //high column start address
		for(n=0;n<128;n++)
		{
			WriteDat(fill_Data);
		}
	}
}

void OLED_Init(void)
{
	vTaskDelay(10 / 1000);

	WriteCmd(0xAE); //display off
	WriteCmd(0x20); //Set Memory Addressing Mode    
	WriteCmd(0x10); //00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
	WriteCmd(0xb0); //Set Page Start Address for Page Addressing Mode,0-7
	WriteCmd(0xc8); //Set COM Output Scan Direction
	WriteCmd(0x00); //---set low column address
	WriteCmd(0x10); //---set high column address
	WriteCmd(0x40); //--set start line address
	WriteCmd(0x81); //--set contrast control register
	WriteCmd(0xff); //亮度调节 0x00~0xff
	WriteCmd(0xa1); //--set segment re-map 0 to 127
	WriteCmd(0xa6); //--set normal display
	WriteCmd(0xa8); //--set multiplex ratio(1 to 64)
	WriteCmd(0x3F); //
	WriteCmd(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
	WriteCmd(0xd3); //-set display offset
	WriteCmd(0x00); //-not offset
	WriteCmd(0xd5); //--set display clock divide ratio/oscillator frequency
	WriteCmd(0xf0); //--set divide ratio
	WriteCmd(0xd9); //--set pre-charge period
	WriteCmd(0x22); //
	WriteCmd(0xda); //--set com pins hardware configuration
	WriteCmd(0x12);
	WriteCmd(0xdb); //--set vcomh
	WriteCmd(0x20); //0x20,0.77xVcc
	WriteCmd(0x8d); //--set DC-DC enable
	WriteCmd(0x14); //
	WriteCmd(0xaf); //--turn on oled panel
	OLED_Fill(0x00);
}

void OLED_SetPos(unsigned char x, unsigned char y) //设置起始点坐标
{ 
	WriteCmd(0xb0+y);
	WriteCmd(((x&0xf0)>>4)|0x10);
	WriteCmd((x&0x0f)|0x01);
}

void OLED_ON(void)
{
	WriteCmd(0X8D);  //设置电荷泵
	WriteCmd(0X14);  //开启电荷泵
	WriteCmd(0XAF);  //OLED唤醒
}

void OLED_OFF(void)
{
	WriteCmd(0X8D);  //设置电荷泵
	WriteCmd(0X10);  //关闭电荷泵
	WriteCmd(0XAE);  //OLED休眠
}
//--------------------------------------------------------------
// Prototype      : void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize)
// Calls          : 
// Parameters     : x,y -- 起始点坐标(x:0~127, y:0~7); ch[] -- 要显示的字符串; TextSize -- 字符大小(1:6*8 ; 2:8*16)
// Description    : 显示codetab.h中的ASCII字符,有6*8和8*16可选择
//--------------------------------------------------------------
void OLED_ShowStr(unsigned char x, unsigned char y,char ch[], unsigned char TextSize)
{
	unsigned char c = 0,i = 0,j = 0;
	switch(TextSize)
	{
			case 1:
			{
					while(ch[j] != '\0')
					{
							c = ch[j] - 32;
							if(x > 126)
							{
									x = 0;
									y++;
							}
							OLED_SetPos(x,y);
							for(i=0;i<6;i++)
									WriteDat(F6x8[c][i]);
							x += 6;
							j++;
					}
			}break;
			case 2:
			{
					while(ch[j] != '\0')
					{
							c = ch[j] - 32;
							if(x > 120)
							{
									x = 0;
									y++;
							}
							OLED_SetPos(x,y);
							for(i=0;i<8;i++)
									WriteDat(F8X16[c*16+i]);
							OLED_SetPos(x,y+1);
							for(i=0;i<8;i++)
									WriteDat(F8X16[c*16+i+8]);
							x += 8;
							j++;
					}
			}break;
	}
}

//--------------------------------------------------------------
// Prototype      : void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N)
// Calls          : 
// Parameters     : x,y -- 起始点坐标(x:0~127, y:0~7); N:汉字在codetab.h中的索引
// Description    : 显示codetab.h中的汉字,16*16点阵
//--------------------------------------------------------------
void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N ,unsigned char* F16x16)
{
	unsigned char wm=0;
	unsigned int  adder=0;
	unsigned int i=0;
	for(i=0;i<N;i++)
	{
			adder=32*i;
			OLED_SetPos(x+16*i,y);
			for(wm = 0;wm <16;wm++)
			{
					WriteDat(F16x16[adder]);
					adder += 1;
			}
			OLED_SetPos(x+16*i,y + 1);
			for(wm = 0;wm < 16;wm++)
			{
					WriteDat(F16x16[adder]);
					adder += 1;
			}
	}
}


void OLED_CLS(void)//清屏
{
        OLED_ShowStr(0,0,"          ",2);
}

void app_main(void)
{

	ESP_ERROR_CHECK(i2c_master_init());
	ESP_LOGI(TAG, "I2C initialized successfully");
	
	OLED_Init();
	vTaskDelay(10 / 1000);
	OLED_CLS();
	OLED_ShowStr(0,0,"Maziot - OLED!",2);
	OLED_ShowStr(0,4,"hello world!",2);
}
